using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

// ReSharper disable EventNeverSubscribedTo.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable VirtualMemberNeverOverriden.Global

namespace Pyxis.Toolkit.Windows.Commands
{
	public abstract class CommandCore : FrameworkElement, IDisposable, IUiCommand
	{
		private EventHandler mCanExecuteChanged;
		private int mRunningCounter;

		#region Dependency properties

		public static readonly DependencyProperty WaitPanelProperty =
			DependencyProperty.Register("WaitPanel", typeof (Panel), typeof (CommandCore), null);

		public static readonly DependencyProperty IsExecutingProperty =
			DependencyProperty.Register("IsExecuting", typeof (bool), typeof (CommandCore),
			                            new PropertyMetadata(default(bool)));

		private readonly DelayIndicationProvider delayIndicationProvider = new DelayIndicationProvider();

		public Panel WaitPanel
		{
			get { return (Panel) GetValue(WaitPanelProperty); }
			set { SetValue(WaitPanelProperty, value); }
		}

		public bool IsExecuting
		{
			get { return (bool) GetValue(IsExecutingProperty); }
			set { SetValue(IsExecutingProperty, value); }
		}

		#endregion

		#region Properties

		public bool HasWaitPanel
		{
			get { return WaitPanel != null; }
		}

		#endregion

		#region IDisposable

		public void Dispose()
		{
			try
			{
				Dispose(true);
			}
			finally
			{
				GC.SuppressFinalize(this);
			}
		}

		~CommandCore()
		{
			Dispose(false);
		}

		protected void Dispose(Boolean disposing)
		{
			if (disposing)
			{
			}
		}

		#endregion

		#region Events

		public event EventHandler<CancelEventArgs> Executing;

		public event EventHandler<CommandExecutionResultEventArgs> Executed;

		public abstract void Execute(object parameter);

		public abstract bool CanExecute(object parameter);

		public event EventHandler CanExecuteChanged
		{
			add { mCanExecuteChanged += value; }
			remove { mCanExecuteChanged -= value; }
		}

		public void RaiseExecuting(CancelEventArgs cancelEventArgs)
		{
			EventHandler<CancelEventArgs> handler = Executing;
			if (handler != null) handler(this, cancelEventArgs);
		}

		public void RaiseExecuted(CommandExecutionResult commandExecutionResult)
		{
			EventHandler<CommandExecutionResultEventArgs> handler = Executed;
			if (handler != null) handler(this, new CommandExecutionResultEventArgs(commandExecutionResult));
		}

		protected virtual void OnExecuted(CommandExecutionResult commandExecutionResult)
		{
		}

		protected void InvalidateCanExecute()
		{
			if (mCanExecuteChanged != null)
			{
				mCanExecuteChanged(this, EventArgs.Empty);
			}
		}

		protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			base.OnPropertyChanged(e);
			InvalidateCanExecute();
		}

		#endregion

		protected void SetComplete()
		{
			if (mRunningCounter <= 1)
				OnExecutedCore(CommandExecutionResult.FromSuccess());
			else
				DecrementRunningCount();
		}

		protected void SetFailure(Exception exception)
		{
			mRunningCounter = 1;
			OnExecutedCore(CommandExecutionResult.FromException(exception));
		}

		internal void OnExecutedCore(CommandExecutionResult commandExecutionResult)
		{
			try
			{
				DecrementRunningCount();

				OnExecuted(commandExecutionResult);
				RaiseExecuted(commandExecutionResult);
			}
			catch (Exception e)
			{
				RaiseExecuted(CommandExecutionResult.FromException(e));
			}
			finally
			{
				IsExecuting = false;
			}
		}

		protected void DecrementRunningCount()
		{
			mRunningCounter--;
			if (mRunningCounter <= 0 && HasWaitPanel)
				delayIndicationProvider.HideBusyIndicator(WaitPanel);
		}

		internal bool OnExecutingCore()
		{
			IncrementRunningCount();

			var cancelEventArgs = new CancelEventArgs
			{
				Cancel = !OnExecuting()
			};
			RaiseExecuting(cancelEventArgs);
			IsExecuting = !cancelEventArgs.Cancel;
			return IsExecuting;
		}

		protected void IncrementRunningCount()
		{
			mRunningCounter++;
			if (HasWaitPanel)
				delayIndicationProvider.ShowBusyIndicator(WaitPanel);
		}

		protected virtual bool OnExecuting()
		{
			return true;
		}

		#region Nested type: CommandExecutionResultEventArgs

		public class CommandExecutionResultEventArgs : EventArgs
		{
			private readonly CommandExecutionResult mCommandExecutionResult;

			public CommandExecutionResultEventArgs(CommandExecutionResult commandExecutionResult)
			{
				mCommandExecutionResult = commandExecutionResult;
			}

			public CommandExecutionResult CommandExecutionResult
			{
				get { return mCommandExecutionResult; }
			}
		}

		#endregion
	}
}

// ReSharper restore EventNeverSubscribedTo.Global
// ReSharper restore UnusedMember.Global
// ReSharper restore VirtualMemberNeverOverriden.Global